package com.project.springboot.service.transaction.impl;

import com.project.springboot.dataobject.Product;
import com.project.springboot.dataobject.UserInfo;
import com.project.springboot.exception.ResponseException;
import com.project.springboot.repository.ProductRepository;
import com.project.springboot.repository.UserInfoRepository;
import com.project.springboot.service.transaction.TransactionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;

/**
 * description: 测试事务服务接口实现类
 * @author 朱正磊
 * @date 2023-06-14 21:49:52
 */
@Slf4j
@Service
public class TransactionServiceImpl implements TransactionService {

    @Resource
    private ProductRepository productRepository;

    @Resource
    private UserInfoRepository userInfoRepository;

    @Resource
    private PlatformTransactionManager transactionManager;

    @Resource
    private TransactionTemplate transactionTemplate;

    /**
     * description: 测试一：使用声明式事务注解@Transactional
     * @author 朱正磊
     * @date 2023-06-14 22:03:06
     * @param pName 商品名
     * @param userName 用户名
     */
    @Override
    public void test1(String pName, String userName) {
        Product product = productRepository.getOne(1);
        product.setPname(pName);
        UserInfo userInfo = userInfoRepository.getOne(1);
        userInfo.setUserName(userName);

        // 使用AopContext（本质上使用的是一个ThreadLocal）
        TransactionServiceImpl currentProxy = (TransactionServiceImpl) AopContext.currentProxy();
        // currentProxy的class：class com.project.springboot.service.transaction.impl.TransactionServiceImpl$$EnhancerBySpringCGLIB$$e91453f1
        log.info("currentProxy的class：{}", currentProxy.getClass());
        currentProxy.write(product, userInfo);
    }

    @Transactional(rollbackFor = Throwable.class)
    public void write(Product product, UserInfo userInfo) {
        productRepository.save(product);
        userInfoRepository.save(userInfo);
        //手动设置异常
        int i = 1 / 0;
    }

    /**
     * description: 测试二：使用编程式事务PlatformTransactionManager
     * @author 朱正磊
     * @date 2023-06-14 22:03:11
     * @param pName 商品名
     * @param userName 用户名
     */
    @Override
    public void test2(String pName, String userName) {
        Product product = productRepository.getOne(2);
        product.setPname(pName);
        UserInfo userInfo = userInfoRepository.getOne(2);
        userInfo.setUserName(userName);

        // 事务基础信息 超时时间、隔离级别、传播属性等
        // 定义事务属性
        DefaultTransactionDefinition transDef = new DefaultTransactionDefinition();
        // 设置传播行为属性， 默认事务级别。 当前无事务则新建事务，已有事务则加入事务。
        transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
        // 获得事务状态
        TransactionStatus status = transactionManager.getTransaction(transDef);
        try {
            log.info("执行事务方法");
            productRepository.save(product);
            userInfoRepository.save(userInfo);
            //手动设置异常
            int i = 1 / 0;
            // 提交
            transactionManager.commit(status);
        } catch (Exception e) {
            log.info("执行事务异常，需要回滚");
            // 回滚
            transactionManager.rollback(status);
            throw new ResponseException(500, e.getMessage());
        }
    }

    /**
     * description: 测试三：使用编程式事务TransactionTemplate，有返回值
     * @author 朱正磊
     * @date 2023-06-16 09:25:50
     * @param pName 商品名
     * @param userName 用户名
     */
    @Override
    public void test3(String pName, String userName) {
        Product product = productRepository.getOne(3);
        product.setPname(pName);
        UserInfo userInfo = userInfoRepository.getOne(3);
        userInfo.setUserName(userName);

        Boolean result = transactionTemplate.execute(status -> {
            try {
                log.info("执行事务方法");
                productRepository.save(product);
                userInfoRepository.save(userInfo);
                //手动设置异常
                int i = 1 / 0;
                return true;
            } catch (Exception e) {
                log.info("执行事务异常，需要回滚");
                status.setRollbackOnly();
                return false;
            }
        });

        if (result != null && !result) {
            throw new ResponseException(500, "error");
        }
    }

    /**
     * description: 测试四：使用编程式事务TransactionTemplate，没有返回值
     * @author 朱正磊
     * @date 2023-06-16 09:24:05
     * @param pName 商品名
     * @param userName 用户名
     */
    @Override
    public void test4(String pName, String userName) {
        Product product = productRepository.getOne(4);
        product.setPname(pName);
        UserInfo userInfo = userInfoRepository.getOne(4);
        userInfo.setUserName(userName);

        //noinspection NullableProblems
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    log.info("执行事务方法");
                    productRepository.save(product);
                    userInfoRepository.save(userInfo);
                    //手动设置异常
                    int i = 1 / 0;
                } catch (Exception e) {
                    log.info("执行事务异常，需要回滚");
                    status.setRollbackOnly();
                    throw new ResponseException(500, e.getMessage());
                }
            }
        });
    }

}
